﻿using Business.Base;
using Business.Base.Enums;
using Business.Base.Model;
using Business.Base.Service;
using Configuration;
using PLC;
using PLC.Base;
using PumpAlgorithm;
using System;
using System.Collections;
using System.Collections.Generic;


namespace Business.ScrewPump.Perf
{
    public class ScrewPumpPerfExperimentCaculateService: ICaculateService
    {
        #region 私有函数
        /// <summary>
        /// 返回列表中的最大值和最小值，第一个是最小值，第二个是最大值
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        private double[] getPeak(Experiment exp)
        {
            ScrewPumpPerfExperiment experiment = (ScrewPumpPerfExperiment)exp;
            List<ExperimentRecord> list = experiment.RecordList;
            if (list == null || list.Count == 0)
            {
                return null;
            }

            Double max, min;
            min = Double.MaxValue;
            max = Double.MinValue;

            Double[] presureList = new Double[list.Count];
            for (int i = 0; i < list.Count; i++)
            {
                ScrewPumpPerfExperimentRecord record = (ScrewPumpPerfExperimentRecord)list[i];
                presureList[i] = record.StpPresure;


                //找出最大最小的压力值
                if (presureList[i] - max > Double.Epsilon)
                {
                    max = presureList[i];
                }
                if (min - presureList[i] > Double.Epsilon)
                {
                    min = presureList[i];
                }
            }
            return new double[] { min,max};
        }

        /// <summary>
        /// 求压力流量，压力功率，压力效率拟合曲线的参数
        /// </summary>
        /// <param name="exp"></param>
        /// <returns></returns>
        private List<Double[]> calculateParams(Experiment exp)
        {
            ScrewPumpPerfExperiment experiment = (ScrewPumpPerfExperiment)exp;
            List<ExperimentRecord> list = experiment.RecordList;
            if (list == null || list.Count == 0)
            {
                return null;
            }

            Double[] presureList = new Double[list.Count];
            Double[] qList = new Double[list.Count];
            Double[] pList = new Double[list.Count];
            Double[] eList = new Double[list.Count];

            //最终报表中展示的都是标况下的数据
            for (int i = 0; i < list.Count; i++)
            {
                ScrewPumpPerfExperimentRecord record = (ScrewPumpPerfExperimentRecord)list[i];
                qList[i] = record.StpFlow;
                presureList[i] = record.StpPresure;

                pList[i] = record.StpShaftPower;
                eList[i] = record.StpPumpEfficiency;
            }


            Double[] paramQ = CanonicalAlgorithm.Solve(presureList, qList);
            Double[] paramP = CanonicalAlgorithm.Solve(presureList, pList);
            Double[] paramE = CanonicalAlgorithm.Solve(presureList, eList);

            List<Double[]> paramsList = new List<Double[]>();
            paramsList.Add(paramQ);
            paramsList.Add(paramP);
            paramsList.Add(paramE);

            return paramsList;
        }

        /// <summary>
        /// 求以下两个曲线的焦点：
        /// 1. 额定压力和流量的曲线 y=ax (其中a = ratedFlow/ratedPresure) 
        /// 2. 拟合的压力流量曲线 y = paramQ[0] * sqrt(x,4) +  paramQ[1] * sqrt(x,3)+paramQ[2] * sqrt(x,3)+ paramQ[3] * sqrt(x,2)+ paramQ[1] * sqrt(x,1) + paramQ[0]
        /// 该交点称作压力和流量实测的坐标点
        /// </summary>
        /// <param name="exp"></param>
        /// <param name="paramQ"></param>
        /// <returns></returns>
        private Double[] calculateCrossPresureFlow(Experiment exp,Double[] paramQ)
        {
            Double minPress, maxPress;
            Double[] peak = getPeak(exp);
            minPress = peak[0];
            maxPress = peak[1];
            ScrewPumpPerfExperiment experiment = (ScrewPumpPerfExperiment)exp;
            ///求以下两个曲线的焦点：
            /// 1. 额定压力和流量的曲线 y=ax (其中a = ratedFlow/ratedPresure) 
            /// 2. 拟合的压力流量曲线 y = paramQ[0] * sqrt(x,4) +  paramQ[1] * sqrt(x,3)+paramQ[2] * sqrt(x,3)+ paramQ[3] * sqrt(x,2)+ paramQ[1] * sqrt(x,1) + paramQ[0]
            /// 该交点称作压力和流量实测的坐标点
            List<Double[]> crossFlowValue = CanonicalAlgorithm.SolveEquations(experiment.PumpRatedFlow/ experiment.PumpRatedPresure, 0, minPress, maxPress, paramQ);
            Double presure = -1;
            Double flow = -1; ;
            if (crossFlowValue.Count==1)
            {
                presure = crossFlowValue[0][0];
                flow = crossFlowValue[0][1];
            }
            return new double[] { presure, flow };
        }
        
        #endregion

        public void CaculateExperimentResult(ref Experiment exp)
        {
            ScrewPumpPerfExperiment experiment = (ScrewPumpPerfExperiment)exp;
            List<Double[]> listParams = calculateParams(exp);
            if (listParams == null)
            {
                return;
            }

            Double[] paramQ = listParams[0];
            Double[] paramP = listParams[1];
            Double[] paramE = listParams[2];

            ///计算流量压力的实测值
            double[] presureFlow = calculateCrossPresureFlow(exp, paramQ);
            experiment.MeasurePresure = presureFlow[0];
            experiment.MeasureFlow = presureFlow[1];

            /// 将额压力和流量实测的坐标点的压力值带入压力功率拟合曲线，求出实测功率
            Double measuredPower = CanonicalAlgorithm.CaculateValue(experiment.MeasurePresure, paramP);
            bool powerLevel = StandLevelJudger.JudgeFlowLevel(measuredPower, experiment.PumpRatedPower, experiment.ExperimentLevel);
            experiment.MeasurePower = (float)measuredPower;
            experiment.MeasurePowerIsPass = powerLevel;

            /// 将额压力和流量实测的坐标点的压力值带入压力效率拟合曲线，求出实测效率
            Double measuredEff = CanonicalAlgorithm.CaculateValue(experiment.MeasurePresure, paramE);
            bool effLevel = StandLevelJudger.JudgeFlowLevel(measuredEff, experiment.PumpRatedEfficiency, experiment.ExperimentLevel);
            experiment.MeasureEff = (float)measuredEff;
            experiment.MeasureEffIsPass = effLevel;
            #region 注释
            /*
           ScrewPumpPerfExperiment experiment = (ScrewPumpPerfExperiment)exp;
           List<ExperimentRecord> list = experiment.RecordList;
           if (list == null || list.Count == 0)
           {
               return;
           }

           Double[] presureList = new Double[list.Count];
           Double[] qList = new Double[list.Count];
           Double[] pList = new Double[list.Count];
           Double[] eList = new Double[list.Count];

           Double minPress, maxPress;
           minPress = Double.MaxValue;
           maxPress = Double.MinValue;

           //最终报表中展示的都是标况下的数据
           for (int i = 0; i < list.Count; i++)
           {
               ScrewPumpPerfExperimentRecord record = (ScrewPumpPerfExperimentRecord)list[i];
               qList[i] = record.StpFlow;
               presureList[i] = record.StpPresure;

               pList[i] = record.StpShaftPower;
               eList[i] = record.StpPumpEfficiency;

               //找出最大最小的
               if (presureList[i] - maxPress > Double.Epsilon)
               {
                   maxPress = qList[i];
               }
               if (minPress - presureList[i] > Double.Epsilon)
               {
                   minPress = qList[i];
               }
           }


           Double[] paramQ = CanonicalAlgorithm.Solve(presureList, qList);
           Double[] paramP = CanonicalAlgorithm.Solve(presureList, pList);
           Double[] paramE = CanonicalAlgorithm.Solve(presureList, eList);



          Double curveQ = CanonicalAlgorithm.CaculateValue(experiment.PumpRatedPresure, paramQ);
           bool flowLevel = StandLevelJudger.JudgeFlowLevel(curveQ, experiment.PumpRatedFlow, experiment.ExperimentLevel);
           experiment.MeasureFlow = (float)curveQ;
           experiment.MeasureFlowIsPass = flowLevel;

           //根据额定压力以及拟合的曲线求出曲线上的功率
           Double curveEff = CanonicalAlgorithm.CaculateValue(experiment.PumpRatedPresure, paramE);
           bool effLevel = StandLevelJudger.JudgeFlowLevel(curveEff, experiment.PumpRatedEfficiency, experiment.ExperimentLevel);
           experiment.MeasureEff = (float)curveEff;
           experiment.MeasureEffIsPass = effLevel;

           //根据额定压力以及拟合的曲线求出曲线上的功率
           Double curvePower = CanonicalAlgorithm.CaculateValue(experiment.PumpRatedPresure, paramP);
           bool powerLevel = StandLevelJudger.JudgeFlowLevel(curvePower, experiment.PumpRatedPower, experiment.ExperimentLevel);
           experiment.MeasurePower = (float)curvePower;
           experiment.MeasurePowerIsPass = powerLevel;*/



            /*
            //额定压力和额定功率与拟合的压力功率的曲线的交叉点
            Double[] crossPowerValue = CanonicalAlgorithm.SolveEquations(experiment.PumpRatedPresure, experiment.PumpRatedPower, minPress, maxPress, paramP);
            presure = -1;
            double power = -1; ;
            if (crossPowerValue != null)
            {
                presure = crossPowerValue[0];
                power = crossPowerValue[1];
            }
            experiment.CrossPPPresure = presure;
            experiment.CrossPPPower = power;

            //额定压力和额定功率与拟合的压力功率的曲线的交叉点
            Double[] crossEffValue = CanonicalAlgorithm.SolveEquations(experiment.PumpRatedPresure, experiment.PumpRatedEfficiency, minPress, maxPress, paramE);
            presure = -1;
            double eff = -1; ;
            if (crossPowerValue != null)
            {
                presure = crossEffValue[0];
                eff = crossEffValue[1];
            }
            experiment.CrossPEPresure = presure;
            experiment.CrossPEEff = eff;
            */

            #endregion


        }

        public ExperimentRecord CaculateDataFromRecord(Experiment exp,EnvData data)
        {
            ScrewPumpPerfExperiment experiment = (ScrewPumpPerfExperiment)exp;
            CanonicalAlgorithm canonicalAlgorithm = new CanonicalAlgorithm(experiment.ZInlet,
                experiment.ZOutlet,
                experiment.KInlet,
                experiment.KOutlet,
                experiment.DInlet,
                experiment.DOutlet,
                experiment.LInlet,
                experiment.LOutlet,
                experiment.MotorRatedEfficiency,
                experiment.MotorRatedSpeed);
            canonicalAlgorithm.setplcEnvData(data);

            ScrewPumpPerfExperimentRecord record = new ScrewPumpPerfExperimentRecord();
            record.ElectricCurrent = data.Current;
            record.Voltage = data.Voltage;
            record.WorkingFlow = data.Flow;
            record.Temperature = data.Temperature;
            record.PresureIn = data.PresureIn;
            record.PresureOut = data.PresureOut;
            record.WorkingPower = data.ShaftPower;
            record.WorkingSpeed = data.Speed;

            canonicalAlgorithm.Calculate();

            record.WorkingHead = canonicalAlgorithm.WorkingHead;
            record.StpFlow = canonicalAlgorithm.StpQ;
            record.StpHead = canonicalAlgorithm.StpHead;
            record.StpPowerIn = canonicalAlgorithm.StpPowerIn/1000.0f;
            record.StpShaftPower = canonicalAlgorithm.StpShaftPower/1000.0f;
            
            record.StpPumpEfficiency = canonicalAlgorithm.StpPumpEfficiency;
            record.StpGroupEfficiency = canonicalAlgorithm.StpGroupEfficiency;
            record.WorkingStaticHead = canonicalAlgorithm.WorkingStaticHead;
            record.WorkingDynamicHead = canonicalAlgorithm.WorkingDynamicHead;
            record.WorkingGeoHead = canonicalAlgorithm.WorkingGeoHead;
            record.WorkingFrictionHead = canonicalAlgorithm.WorkingFrictionHead;
            record.PresureTotal = canonicalAlgorithm.TotalPressure;
            record.StpPresure = canonicalAlgorithm.StpPresure;
            return record;
        }



        public BaseChartData CaculateChartData(Experiment exp)
        {
            #region 初始化变量
            ScrewPumpPerfChartData chartDataInfo = new ScrewPumpPerfChartData();
            ScrewPumpPerfExperiment experiment = (ScrewPumpPerfExperiment)exp;

            //设置额定值
            Double ratedFlow    =    experiment.PumpRatedFlow;
            Double ratedPresure =    experiment.PumpRatedPresure;
            Double ratedPower   =    experiment.PumpRatedPower;
            Double ratedEff     =    experiment.PumpRatedEfficiency;
            chartDataInfo.YRatedQ = ratedFlow;
            chartDataInfo.XRatedPressure = ratedPresure;
            chartDataInfo.YRatedPower = ratedPower;
            chartDataInfo.YRatedEff = ratedEff;

            //设置试验的标准
            PresureFlowLevel level = experiment.ExperimentLevel;
            
            //设置坐标基础数据
            int xUnitCount = Config.XAxisUnit;
            int yUnitCount = Config.YAxisUnit;
            double axisMaxRate = Config.AxisMaxRate;
            int drawPointCount = Config.DrawPointCount;          

            //设置坐标轴的最小值
            chartDataInfo.MinXPressAxis = 0.0f;
            chartDataInfo.MinYQAxis = 0.0f;
            chartDataInfo.MinYPAxis = 0.0f;

            chartDataInfo.XUnitCount = xUnitCount;
            chartDataInfo.YUnitCount = yUnitCount;
            #endregion

            List<ExperimentRecord> list = experiment.RecordList;

            //如果还没有采集数据，则设置流量，功率最大值为额定值除以axisMaxRate，axisMaxRate默认配置为80%，保证最大值为额定值的时候，让额定值展示在坐标轴80%的地方。
            if (list.Count == 0)
            {
                chartDataInfo.MaxXPressAxis = ratedPresure/axisMaxRate; 
                chartDataInfo.MaxYQAxis = ratedFlow / axisMaxRate;
                chartDataInfo.MaxYPAxis = ratedPower / axisMaxRate;
            }
            else
            {
                Double[] qList = new Double[list.Count];
                Double[] pressureList = new Double[list.Count];
                Double[] pList = new Double[list.Count];
                Double[] eList = new Double[list.Count];


                //获取最小流量和最大流量
                double maxQY = 0.0f;
                double minQY = Double.MaxValue;

                //获取扬程的最大值和最小值
                Double maxXPressure= 0.0f;
                Double minXPressure = Double.MaxValue;

                //获取Y轴数据的最大值和最小值
                Double maxPY = 0.0f;
                Double minPY = Double.MaxValue;

                Double maxAxisQ = ratedFlow;
                Double maxAxisPresure = ratedPresure;
                Double maxAxisPower = ratedPower;

                List<double[]> pointList = new List<double[]>();
           

                #region 在记录中找出流量，功率，效率的最大值，如果最大值小于额定值，则坐标的最大值为额定功率/axisMaxRate

             
                for (int i = 0; i < list.Count; i++)
                {
                    ScrewPumpPerfExperimentRecord  record = (ScrewPumpPerfExperimentRecord)list[i];
                   
                    qList[i] = record.StpFlow;
                    pressureList[i] = record.StpPresure;
                    pList[i] = record.StpShaftPower;
                    eList[i] = record.StpPumpEfficiency;
                    //Console.WriteLine(record.ToString());
                    #region 求出流量，扬程，功率，效率的最大值和最小值
                    double q = qList[i];
                    double delta = q - maxQY;
                    
                    if (delta > Double.Epsilon)
                    {
                        maxQY = q;
                    }

                    delta = q - minQY;
                    delta = minQY - q;
                    if (delta > Double.Epsilon)
                    {
                        minQY = q;
                    }

                    //压力最大值和最小值
                    Double pressure = (Double)pressureList[i];
                    delta = pressure - maxXPressure;
                    if(delta > double.Epsilon  ) //if (delta >= 0.0001)
                    {
                        maxXPressure = pressure;
                    }

                    //delta = pressure - minXPressure;
                    delta = minXPressure - pressure;
                     //if (delta <= -0.0001)
                    if(delta > double.Epsilon )
                    {
                        minXPressure = pressure;
                    }

                    //功率最大最小值
                    Double py = (Double)pList[i];
                    delta = py - maxPY;
                    if (delta > double.Epsilon)
                    {
                        maxPY = py;
                    }
                    //delta = py - minPY;
                    delta = minPY - py;
                    if (delta > double.Epsilon) //if (delta <= -0.0001)
                    {
                        minPY = py;
                    }
                    #endregion


                    double[] data = new double[4] { pressure,q, py, eList[i] };
                    pointList.Add(data);
                }

                chartDataInfo.InputPoints = pointList;

                maxAxisQ = ratedFlow - maxQY > double.Epsilon ? ratedFlow : maxQY;
                maxAxisPresure = ratedPresure - maxXPressure > double.Epsilon ? ratedPresure : maxXPressure;
                maxAxisPower = ratedPower - maxPY > double.Epsilon ? ratedPower : maxAxisPower;

                /*if (ratedFlow - maxQY > double.Epsilon)
                {
                    maxAxisQ = ratedFlow;
                }
             
                if (ratedPresure - maxXPressure > double.Epsilon)
                {
                    maxAxisPresure = ratedPresure;
                }
                if (ratedPower -  maxPY > double.Epsilon)
                {
                    maxAxisPower = ratedPower;
                }*/



                //求出最大值，并且做一个坐标余量的计算。
                chartDataInfo.MaxXPressAxis = maxAxisPresure / axisMaxRate;
                chartDataInfo.MaxYQAxis = maxAxisQ / axisMaxRate;
                chartDataInfo.MaxYPAxis = maxAxisPower / axisMaxRate;
                #endregion

                
                if (list.Count > 2)
                {
                    #region 根据测试出的数据拟合出曲线
                    Double[] paramQ, paramP, paramE;
                    paramQ = CanonicalAlgorithm.Solve(pressureList, qList);
                    paramP = CanonicalAlgorithm.Solve(pressureList, pList);
                    paramE = CanonicalAlgorithm.Solve(pressureList, eList);
                    #region 如果计算的值不是数字，则不画曲线
                    Boolean needDrawLine = true;
                    for (int i = 0; i < paramQ[i]; i++)
                    {
                        if (Double.IsInfinity(paramQ[i]) || Double.IsNaN(paramQ[i]))
                        {
                            needDrawLine = false;
                        }
                    }

                    for (int i = 0; i < paramP[i]; i++)
                    {
                        if (Double.IsInfinity(paramP[i]) || Double.IsNaN(paramP[i]))
                        {
                            needDrawLine = false;
                        }
                    }

                    for (int i = 0; i < paramE[i]; i++)
                    {
                        if (Double.IsInfinity(paramE[i]) || Double.IsNaN(paramE[i]))
                        {
                            needDrawLine = false;
                        }
                    }

                    #endregion

                    if (needDrawLine)
                    {
                        #region  根据公式，平均选取30个点，用于报表中画曲线的点
                        Double[] xValue = new Double[drawPointCount + 1];
                        Double[] yqValue = new Double[drawPointCount + 1];
                        Double[] ypValue = new Double[drawPointCount + 1];
                        Double[] yeValue = new Double[drawPointCount + 1];
                        double dd = (maxXPressure - minXPressure) / drawPointCount;
                        for (int i = 0; i <= drawPointCount; i++)
                        {
                            xValue[i] = minXPressure + i * dd;
                            Double press = xValue[i];
                            yqValue[i] = CanonicalAlgorithm.CaculateValue(press, paramQ);
                            ypValue[i] = CanonicalAlgorithm.CaculateValue(press, paramP);
                            yeValue[i] = CanonicalAlgorithm.CaculateValue(press, paramE);
                            chartDataInfo.DrawList.Add(new double[4] { xValue[i], yqValue[i], ypValue[i], yeValue[i] });
                            //System.Console.WriteLine("{0}，{1},{2},{3}", xValue[i], yqValue[i], ypValue[i], yeValue[i]);
                        }
                        #endregion

                        
                        #region  计算流量压力的实测值
                        double[] presureFlow = calculateCrossPresureFlow(exp, paramQ);
                        if (presureFlow != null)
                        {
                            chartDataInfo.MeasurePresure = presureFlow[0];
                            chartDataInfo.MeasureFlow = presureFlow[1];

                            /// 将额压力和流量实测的坐标点的压力值带入压力功率拟合曲线，求出实测功率
                            Double measuredPower = CanonicalAlgorithm.CaculateValue(experiment.MeasurePresure, paramP);
                            //experiment.MeasurePower = (float)measuredPower;
                            chartDataInfo.MeasurePower = measuredPower;

                            /// 将额压力和流量实测的坐标点的压力值带入压力效率拟合曲线，求出实测效率
                            Double measuredEff = CanonicalAlgorithm.CaculateValue(experiment.MeasurePresure, paramE);
                            chartDataInfo.MeasureEff = measuredEff;
                        }
                        #endregion
                    }
                    #endregion

                  

                }

                

  
                #region  获取额定压力下的流量，功率，效率的允差范围
         
                chartDataInfo.FlowRange = StandLevelJudger.GetFlowRange(ratedFlow, level);
                chartDataInfo.PowerRange = StandLevelJudger.GetPowerRange(ratedPower, level);
                chartDataInfo.EffRange = StandLevelJudger.GetEffRange(ratedEff, level);
                chartDataInfo.ShowFlowRange = true;
                chartDataInfo.ShowPowerRange = true;
                chartDataInfo.ShowEffRange = true;
                #endregion

                #region 获取额定压力和流量下的直线
                double flow_a = ratedFlow / ratedPresure;               
               // chartDataInfo.YMaxFlowinRatePresureLine = flow_a * chartDataInfo.MaxXPressAxis;
               


                //double headQ_a = ratedHead / ratedFlow;

                double maxY = flow_a * chartDataInfo.MaxXPressAxis;
                if (maxY - chartDataInfo.MaxYQAxis > Double.Epsilon)
                {
                    double maxX = (1 / flow_a) * chartDataInfo.MaxYQAxis;
                    chartDataInfo.XMaxFlowinRatePresureLine = maxX;
                    chartDataInfo.YMaxFlowinRatePresureLine = chartDataInfo.MaxYQAxis;

                }
                else
                {
                    chartDataInfo.XMaxFlowinRatePresureLine = chartDataInfo.MaxXPressAxis;
                    chartDataInfo.YMaxFlowinRatePresureLine = maxY;
                }
                #endregion

            }
            return chartDataInfo;
           
        }

        public void ReCaculateRecord(Experiment exp)
        {
            throw new NotImplementedException();
        }
    }
}
